<?php
/**
 * 
 * Ident Authority wrapper for simple Session storage
 *
 * This class is meant to be used when all auth info is stored in Session.
 * 
 * If a user is not authenticated they are forwarded to a URL. This URL should
 * provide the authentication and use this class to store that information in 
 * session.
 * This class does not to automatic permission mapping.  It requires a map to
 * set. One easy to use class is the X_User_Permission_Map_Array.
 * 
 * @author Alton Crossley <crossleyframework@nogahidebootstrap.com>
 * @package Crossley Framework
 *  
 * @copyright Copyright (c) 2003-2009, Nogaide BootStrap INC. All rights reserved.
 * @license BSD http://opensource.org/licenses/bsd-license.php
 * @version $Id:$
 * 
 */
class X_User_Session_Authority implements X_User_Id_Authority_Interface, X_User_Permission_Authority_Interface
{
    /**
     * Url where user is forwarded to get authenticated
     * @var string
     */
    protected $_sLoginUrl;
    /**
     * common constructor optional login url
     * @param $sLoginUrl
     */
    function __construct($sLoginUrl = null)
    {
        $this->_sLoginUrl = $sLoginUrl;
    }
    /**
     * set the identity on the given user
     * @param unknown_type $oUser
     */
    public function setIdentity(X_User_Interface $oUser)
    {
        $oUser->setName('guest');
        $oUser->setPrintableName('Guest');
        if (isset($_SESSION) && array_key_exists(self::$sSessionNamespace, $_SESSION))
        {
            $aSession &= $_SESSION[self::$sSessionNamespace];
            $oUser->setUid($_SESSION[self::$sSessionNamespace]['id']);
            if (array_key_exists('user_login', $aSession)) $oUser->setName($aSession['user_login']);
            if (array_key_exists('user_email', $aSession)) $oUser->setEmail($aSession['user_email']);
            if (array_key_exists('user_nicename', $aSession)) $oUser->setPrintableName($aSession['user_nicename']);
        }
        return $this;
    }
    
    /**
     * @return the $_sLoginUrl
     */
    public function getLoginUrl()
    {
        return $this->_sLoginUrl;
        return $this;
    }
    /**
     * @param $_sLoginUrl the $_sLoginUrl to set
     */
    public function setLoginUrl($_sLoginUrl)
    {
        $this->_sLoginUrl = $_sLoginUrl;
        return $this;
    }
    /**
     * weather or not to forward via header or javascript(default)
     * @var boolean
     */
    protected $_bHeaderForward = true;
    /**
     * @return boolean
     */
    public function getHeaderForward()
    {
        return $this->_bHeaderForward;
        return $this;
    }
    /**
     * @param boolean $_bHeaderForward
     */
    public function setHeaderForward($bHeaderForward)
    {
        $this->_bHeaderForward = $bHeaderForward;
        return $this;
    }
    /**
     * verify session is started and start it if it is not
     * @return string Session Id
     */
    protected function _checkSession()
    {
        $sSessionId = session_id();
        if (empty($sSessionId))
        {
            session_start();
            $sSessionId = session_id();
        }
        return $sSessionId;
    }
    /**
     * @param $sId
     */
    public function getUserIdData($sId)
    {
        throw new X_User_Exception(__METHOD__ . ' not implemented');
    }
    /**
     * @param $xId
     */
    public function varifyUid($xId)
    {
        throw new X_User_Exception(__METHOD__ . ' not implemented');
    }
    /**
     * @param $sToken
     */
    public function verifyToken($sToken)
    {
        throw new X_User_Exception(__METHOD__ . ' not implemented');
    }
    /**
     * @param $sUsername
     * @param $sPassword
     */
    public function verifyTokenPair($sUsername, $sPassword)
    {
        throw new X_User_Exception(__METHOD__ . ' not implemented');
    }
    /**
     * this method knows how to login the user (identify/authenticate)
     * ex. forward to a login page
     * 
     * @see X_User_Interface
     * @return String forward url
     */
    public function identify()
    {
        $sRedirectUrl = urlencode(X_Url::getCurrent());
        $sForwarUrl = '/' . $this->_sLoginUrl . '?redirect_to=' . $sRedirectUrl;
        if ($this->_bHeaderForward)
        {
            // no we dont send a status code, no known applicable
            header('Location: ' . $sForwarUrl);
            exit(0);
        }
        return $sForwarUrl;
    }
    /**
     * @param $oUser
     */
    public function verifyUser(X_User_Interface $oUser, $bUseSession = true)
    {
        if ($bUseSession && $this->verifyUserSession($oUser)) return true;
        // @todo check the user data against other ways eg. database
        return false;
    }
    /**
     * key in the session where wordpress user info is stored
     * @var unknown_type
     */
    public static $sSessionNamespace = 'x-user';
    /**
     * @param $oUser
     */
    public function verifyUserSession(X_User_Interface $oUser)
    {
        $this->_checkSession();
        if (array_key_exists(self::$sSessionNamespace, $_SESSION))
        {
            // see if the user id matches the session id
            if (array_key_exists('id', $_SESSION[self::$sSessionNamespace]))
            {
                if ($oUser->getUid() == $_SESSION[self::$sSessionNamespace]['id'])
                {
                    return true;
                }
                // the current user object is not set, but the session may be valid
                // set the session info into the user overwriting the current user with the session
                $this->setIdentity($oUser);
                return true;
            }
        }
        // there is no valid session
        return false;
    }
    /**
     * set user parameters on the session
     * @param $oUser
     */
    public function setUserSession(X_User_Interface $oUser)
    {
        $this->_checkSession();
        $_SESSION[self::$sSessionNamespace]['id'] = $oUser->getUid();
        $_SESSION[self::$sSessionNamespace]['user_login'] = $oUser->getName();
        $_SESSION[self::$sSessionNamespace]['user_email'] = $oUser->getEmail();
        $_SESSION[self::$sSessionNamespace]['user_nicename'] = $oUser->getPrintableName();
        return $this;
    }
    /**
     * @var X_User_Permission_Map_Interface
     */
    private $_PermissionMap;
    /**
     * @return the $_PermissionMap
     */
    function getPermissionMap()
    {
        return $this->_PermissionMap;
    }
    /**
     * @param $_PermissionMap the $_PermissionMap to set
     */
    function setPermissionMap(X_User_Permission_Map_Interface $PermissionMap)
    {
        $this->_PermissionMap = $PermissionMap;
        return $this;
    }
    function setMembership($sGroupName, $bIsMember = true)
    {
        $_SESSION[self::$sSessionNamespace]['groups'][$sGroupName] = $bIsMember;
        return $this;
    }
    /**
     * check to see if the user is valid
     * right now subject and detail are combined like detail_subject
     * 
     * @param X_User_Interface $oUser
     * @param $sSubjectUri
     * @param $sDetail
     */
    public function isPermitted(X_User_Interface $oUser, $sSubjectUri, $sDetail = null)
    {
        $this->_checkSession();
        if ($this->verifyUserSession($oUser))
        {
            if (isset($this->_PermissionMap))
            {
                $sPermKey = $this->_buildPermKey($sSubjectUri, $sDetail);
                $sGroup = $this->_PermissionMap->map($sPermKey);
                if (array_key_exists($sGroup, $_SESSION[self::$sSessionNamespace]['groups']))
                {
                    // groups are T/F values, true means they are a member, 
                    // false means they are explicitly blocked
                    return $_SESSION[self::$sSessionNamespace]['groups'][$sGroup];
                }
                else
                {
                    return false;
                }
            }
            else
            {
                // @todo check permissions 
                throw new X_User_Exception(__METHOD__ . ' not implemented');
            }
        }
        else
        {
            // user is bad or there is no session
            return false;
        }
    }
    /**
     * combined like detail_subject
     * 
     * @param string $sSubjectUri
     * @param string $sDetail
     * @return string
     */
    protected function _buildPermKey($sSubjectUri, $sDetail)
    {
        return X_String::toCode($sDetail . ' ' . $sSubjectUri, true);
    }
        
}